﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using StyleMVVM.Data;
using StyleMVVM.Data.Activation;
using StyleMVVM.DependencyInjection;
using StyleMVVM.Logging;
using StyleMVVM.Utilities;
#if !DOT_NET
using Windows.Foundation;
#endif
#if NETFX_CORE
using Windows.UI.Xaml;
using Windows.Data.Xml.Dom;

#else
using System.Windows;

#endif

namespace StyleMVVM.Configuration
{
	public sealed class AppSettingConfigurationStorageProvider : IConfigurationStorageProvider, IActivationAware
	{
		private static readonly string supplemental = typeof(AppSettingConfigurationStorageProvider).FullName;
		private readonly Dictionary<string, string> appSettings = new Dictionary<string, string>();

		public static void RegisterExport(IDependencyInjectionContainer container)
		{
			container.Register(typeof(AppSettingConfigurationStorageProvider)).
			          As(typeof(IConfigurationStorageProvider));
		}

		public void Activate()
		{
			try
			{
				if (Application.Current != null)
				{
					string resourceName = Application.Current.GetType().Namespace + ".AppSettings.xml";

					try
					{
						using (Stream appSettingsStream =
							Application.Current.GetType().GetTypeInfo().Assembly.GetManifestResourceStream(resourceName))
						{
							if (appSettingsStream != null)
							{
								Logger.Info("Processing AppSettings file: " + resourceName, supplemental);

								using (StreamReader reader = new StreamReader(appSettingsStream))
								{
									ProcessStreamReader(reader);
								}
							}
							else
							{
								Logger.Info("No AppSettings.xml file found at " + resourceName, supplemental);
							}
						}
					}
					catch (Exception exp)
					{
						Logger.Error("Exception thrown while trying to process: " + resourceName, supplemental, exp);
					}
				}
			}
			catch (Exception exp)
			{
				Logger.Error("Exception thrown while ");
			}
		}

		public bool CanSet
		{
			get { return false; }
		}

#if NETFX_CORE
		public IAsyncOperation<ConfigValuePair> GetValue(string valueName, Type valueType)
		{
			return InternalGetValue(valueName, valueType).AsAsyncOperation();
		}

		public IAsyncOperation<bool> SetValue(string valueName, object newValue)
		{
			return AsyncHelper.ReturnAsyncOp(false);
		}

		public IAsyncOperation<ConfigValuePair> GetUserValue(string username, string valueName, Type valueType)
		{
			return AsyncHelper.ReturnAsyncOp(new ConfigValuePair { ReturnValue = false });
		}

		public IAsyncOperation<bool> SetUserValue(string username, string valueName, object newValue)
		{
			return AsyncHelper.ReturnAsyncOp(false);
		}
#else
		public Task<ConfigValuePair> GetValue(string valueName, Type valueType)
		{
			return InternalGetValue(valueName, valueType);
		}

		public Task<bool> SetValue(string valueName, object newValue)
		{
			return TaskHelper.ReturnTask(false);
		}

		public Task<ConfigValuePair> GetUserValue(string username, string valueName, Type valueType)
		{
			return TaskHelper.ReturnTask(new ConfigValuePair { ReturnValue = false });
		}

		public Task<bool> SetUserValue(string username, string valueName, object newValue)
		{
			return TaskHelper.ReturnTask(false);
		}
#endif

		private async Task<ConfigValuePair> InternalGetValue(string valueName, Type tType)
		{
			bool returnValue = false;
			object returnT = null;
			string appSettingValue;

			if (appSettings.TryGetValue(valueName, out appSettingValue))
			{
				if (tType != typeof(string))
				{
					if (tType.GetTypeInfo().IsPrimitive)
					{
						returnT = Convert.ChangeType(appSettingValue, tType);
					}
					else if (tType.GetTypeInfo().IsEnum)
					{
						returnT = Enum.Parse(tType, appSettingValue);
					}
					else
					{
						DataContractSerializer serializer =
							new DataContractSerializer(tType, ReflectionService.KnownTypes);

						using (MemoryStream ms = new MemoryStream())
						{
							using (StreamWriter newStream = new StreamWriter(ms))
							{
								newStream.Write(appSettingValue);
							}

							ms.Position = 0;

							returnT = serializer.ReadObject(ms);
						}
					}
				}
				else
				{
					returnT = appSettingValue;
				}

				returnValue = true;
			}

			return new ConfigValuePair { ConfigValue = returnT, ReturnValue = returnValue };
		}

#if NETFX_CORE
		private void ProcessStreamReader(StreamReader reader)
		{
			XmlDocument document = new XmlDocument();

			document.LoadXml(reader.ReadToEnd());

			IXmlNode appSettingsNode = null;

			foreach (IXmlNode childNode in document.ChildNodes)
			{
				if (childNode.NodeName == "appSettings")
				{
					appSettingsNode = childNode;
				}
			}
			if (appSettingsNode != null && appSettingsNode.ChildNodes != null)
			{
				foreach (IXmlNode childNode in appSettingsNode.ChildNodes)
				{
					if (childNode.NodeName == "appSetting")
					{
						string name = null;

						foreach (IXmlNode attribute in childNode.Attributes)
						{
							if (attribute.NodeName == "name" && attribute.NodeValue != null)
							{
								name = attribute.NodeValue.ToString();
								break;
							}
						}

						if (!string.IsNullOrEmpty(name))
						{
							Logger.Debug("Found AppSetting: " + name);

							appSettings[name] = childNode.InnerText;
						}
					}
				}
			}
		}
#else
		private void ProcessStreamReader(StreamReader reader)
		{
			string documentText = reader.ReadToEnd();

			XDocument document = XDocument.Parse(documentText);

			XElement appSettingsNode = null;

			foreach (XElement childNode in document.Elements())
			{
				if (childNode.Name == "appSettings")
				{
					appSettingsNode = childNode;
				}
			}
			if (appSettingsNode != null)
			{
				foreach (XElement childNode in appSettingsNode.Elements())
				{
					if (childNode.Name == "appSetting")
					{
						string name = null;

						foreach (XAttribute attribute in childNode.Attributes())
						{
							if (attribute.Name == "name")
							{
								name = attribute.Value;
								break;
							}
						}

						if (!string.IsNullOrEmpty(name))
						{
							appSettings[name] = childNode.Value;
						}
					}
				}
			}
		}
#endif
	}
}